package org.quipu.client;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable;
import static com.google.gwt.user.client.ui.HasHorizontalAlignment.ALIGN_CENTER;
import static com.google.gwt.user.client.ui.HasHorizontalAlignment.ALIGN_LEFT;
import static com.google.gwt.user.client.ui.HasHorizontalAlignment.ALIGN_RIGHT;
import static com.google.gwt.user.client.ui.HasVerticalAlignment.ALIGN_MIDDLE;
import static com.google.gwt.user.client.ui.HasVerticalAlignment.ALIGN_TOP;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.DateBox;
import org.quipu.AppException;
import org.quipu.client.events.AppEvent;
import org.quipu.client.events.AppEventBus;
import org.quipu.client.events.AppEventListener;
import org.quipu.client.events.BookSelectionChangedEvent;
import org.quipu.client.events.TransactionChangedEvent;
import org.quipu.client.events.TransactionSelectionChangedEvent;
import org.quipu.dto.CategoryDto;
import org.quipu.dto.TransactionDto;

public final class TransactionEditPanel extends AppEventWidget implements AppEventListener {

    private final VerticalPanel vPanel;
    private TransactionDto editTransaction;
    private final TransactionDto newTransaction;
    private TransactionDto.Type newTransactionType;
    private final FlexTable form;
//    private final Form form2;
    private final RadioButton incomeRadioBtn;
    private final RadioButton outcomeRadioBtn;
    private final RadioButton transferRadioBtn;
    private final DateBox dateBox;
    private final AccountListBox srcAccountListBox;
    private final AccountListBox dstAccountListBox;
    private final CategoryListBox incomeCategoryListBox;
    private final CategoryListBox outcomeCategoryListBox;
    private final TextBox sumBox;
    private final CurrencyListBox currencyListBox;
    private final CheckBox convertCheckBox;
    private final CurrencyListBox convertCurrencyListBox;
    private final TextBox convertRateBox;
    private final HorizontalPanel convertPanel;
    private final TextArea descArea;
    private final CheckBox approveCheckBox;
    private final TagsCloud tagsCloud;
    private final Button submitBtn;
    private final Button detachBtn;
    private final Button removeBtn;

    public TransactionEditPanel(AppEventBus eventBus, AppCache cache) {
        super(eventBus, cache);
        vPanel = new VerticalPanel();
        vPanel.addStyleName("controlPanel");
        vPanel.setHorizontalAlignment(ALIGN_LEFT);
        vPanel.setVerticalAlignment(ALIGN_TOP);
        vPanel.setSize("100%", "100%");

        form = new FlexTable();
//        form.setBorderWidth(1);
        vPanel.add(form);
//        form2 = new Form(eventBus, cache);
//        vPanel.add(form2.getWidget());

        editTransaction = newTransaction = new TransactionDto();
        newTransactionType = TransactionDto.Type.OUTCOME;
        outcomeRadioBtn = new RadioButton("transactionTypeGroup", "Расход");
        outcomeRadioBtn.setValue(true);
        incomeRadioBtn = new RadioButton("transactionTypeGroup", "Доход");
        transferRadioBtn = new RadioButton("transactionTypeGroup", "Перенос");
        ValueChangeHandler<Boolean> valueChangeHandler = new ValueChangeHandler<Boolean>() {
            public void onValueChange(ValueChangeEvent<Boolean> event) {
                changeTransactionType();
                repaintForm();
            }
        };
        outcomeRadioBtn.addValueChangeHandler(valueChangeHandler);
        incomeRadioBtn.addValueChangeHandler(valueChangeHandler);
        transferRadioBtn.addValueChangeHandler(valueChangeHandler);
        dateBox = new DateBox();
        dateBox.addStyleName("dateInput");
        dateBox.setFormat(new DateBox.DefaultFormat(new DateTimeFormat("dd.MM.yyyy")));
        srcAccountListBox = new AccountListBox(eventBus, cache, null, null);
        dstAccountListBox = new AccountListBox(eventBus, cache, null, null);
        incomeCategoryListBox = new CategoryListBox(eventBus, cache, CategoryDto.Type.INCOME, null);
        outcomeCategoryListBox = new CategoryListBox(eventBus, cache, CategoryDto.Type.OUTCOME, null);
        sumBox = new TextBox();
        sumBox.addStyleName("sumInput");
        currencyListBox = new CurrencyListBox(eventBus, cache, null);

        convertPanel = new HorizontalPanel();
        convertPanel.setVerticalAlignment(ALIGN_MIDDLE);
        convertPanel.setSpacing(3);
        convertRateBox = new TextBox();
        convertRateBox.addStyleName("rateInput");
        convertRateBox.setValue("1");
        convertCurrencyListBox = new CurrencyListBox(eventBus, cache, null);
        convertCheckBox = new CheckBox("списать в валюте");
        convertCheckBox.setValue(false);
        convertCheckBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
            @Override
            public void onValueChange(ValueChangeEvent<Boolean> event) {
                refreshConvertionForm();
            }
        });

        descArea = new TextArea();
        descArea.addStyleName("descriptionAreaInput");
        approveCheckBox = new CheckBox("утвердить");
        approveCheckBox.setValue(true);
        tagsCloud = new TagsCloud(eventBus, cache, true);
        submitBtn = new Button();
        submitBtn.setStylePrimaryName("pushButton");
        submitBtn.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                submitTransaction();
            }
        });
        removeBtn = new Button("Удалить");
        removeBtn.setStylePrimaryName("pushButton");
        removeBtn.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                removeTransaction();
            }
        });
        detachBtn = new Button("Выделить");
        detachBtn.setStylePrimaryName("pushButton");
        detachBtn.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                detachTransaction();
            }
        });
        setValue(null);
    }

    @Override
    public void init() {
        srcAccountListBox.init();
        dstAccountListBox.init();
        incomeCategoryListBox.init();
        outcomeCategoryListBox.init();
        currencyListBox.init();
        convertCurrencyListBox.init();
        tagsCloud.init();
//        form2.init();
        getEventBus().addListener(TransactionSelectionChangedEvent.class, this);
        getEventBus().addListener(BookSelectionChangedEvent.class, this);
    }

    @Override
    public Widget getWidget() {
        return vPanel;
    }

    @Override
    public void handleEvent(AppEvent event) {
        if(event != null) {
            if(event instanceof TransactionSelectionChangedEvent) {
                TransactionDto setTransaction = ((TransactionSelectionChangedEvent) event).getObject();
                setValue(setTransaction != null? (TransactionDto) setTransaction.clone(): null);
            } else if(event instanceof BookSelectionChangedEvent) {
                setValue(newTransaction);
            }
        }
    }

    private void repaintForm() {
        form.removeAllRows();
        final FlexTable.FlexCellFormatter cellFormatter = form.getFlexCellFormatter();

        int row = 0;
        form.setWidget(row, 0, incomeRadioBtn);
        cellFormatter.setStyleName(row, 0, "nowrap");
        form.setWidget(row, 1, outcomeRadioBtn);
        cellFormatter.setColSpan(row, 1, 2);
        cellFormatter.setStyleName(row, 1, "nowrap");
        form.setWidget(row, 2, transferRadioBtn);
        cellFormatter.setStyleName(row++, 2, "nowrap");
        form.setWidget(row, 0, new Label("Дата:"));
        form.setWidget(row, 1, dateBox);
        cellFormatter.setColSpan(row++, 1, 3);

        if(incomeRadioBtn.getValue()) {
            form.setWidget(row, 0, dstAccountListBox.getWidget());
            cellFormatter.setColSpan(row, 0, 2);
            form.setWidget(row, 1, incomeCategoryListBox.getWidget());
            cellFormatter.setColSpan(row++, 1, 2);
        } else if(outcomeRadioBtn.getValue()) {
            form.setWidget(row, 0, srcAccountListBox.getWidget());
            cellFormatter.setColSpan(row, 0, 2);
            form.setWidget(row, 1, outcomeCategoryListBox.getWidget());
            cellFormatter.setColSpan(row++, 1, 2);
        } else {
            form.setWidget(row, 0, srcAccountListBox.getWidget());
            cellFormatter.setColSpan(row, 0, 2);
            form.setWidget(row, 1, dstAccountListBox.getWidget());
            cellFormatter.setColSpan(row++, 1, 2);
        }

        form.setWidget(row, 0, sumBox);
        cellFormatter.setColSpan(row, 0, 2);
        form.setWidget(row, 1, currencyListBox.getWidget());
        cellFormatter.setColSpan(row++, 1, 2);
        form.setWidget(row, 0, convertPanel);
        cellFormatter.setColSpan(row++, 0, 4);
        form.setWidget(row, 0, descArea);
        cellFormatter.setColSpan(row++, 0, 4);
        form.setWidget(row, 0, approveCheckBox);
        cellFormatter.setColSpan(row++, 0, 4);
        form.setWidget(row, 0, tagsCloud.getWidget());
        cellFormatter.setColSpan(row++, 0, 4);
        int col = 0;
        if(!editTransaction.isNew()) {
            form.setWidget(row, col, removeBtn);
            cellFormatter.setAlignment(row, col++, ALIGN_LEFT, ALIGN_MIDDLE);
            form.setWidget(row, col, detachBtn);
            cellFormatter.setColSpan(row, col, 2);
            cellFormatter.setAlignment(row, col++, ALIGN_CENTER, ALIGN_MIDDLE);
        }
        form.setWidget(row, col, submitBtn);
        if(col == 0) {
            cellFormatter.setColSpan(row, col, 4);
        }
        cellFormatter.setAlignment(row, col, ALIGN_RIGHT, ALIGN_MIDDLE);
    }

    private void refreshConvertionForm() {
        convertPanel.clear();
        convertPanel.add(convertCheckBox);
        if(convertCheckBox.getValue()) {
            convertPanel.add(convertCurrencyListBox.getWidget());
            convertPanel.add(new Label("Курс:"));
            convertPanel.add(convertRateBox);
        }
    }

    private void refillForm() {
        if(editTransaction.isNew()) {
            if(TransactionDto.Type.INCOME.equals(newTransactionType)) {
                srcAccountListBox.selectValue(null);
                dstAccountListBox.selectValue(editTransaction.getDstAccountId());
                incomeCategoryListBox.selectValue(editTransaction.getCategoryId());
                outcomeCategoryListBox.selectValue(null);
                incomeRadioBtn.setValue(true);
            } else if(TransactionDto.Type.OUTCOME.equals(newTransactionType)) {
                srcAccountListBox.selectValue(editTransaction.getSrcAccountId());
                dstAccountListBox.selectValue(null);
                incomeCategoryListBox.selectValue(null);
                outcomeCategoryListBox.selectValue(editTransaction.getCategoryId());
                outcomeRadioBtn.setValue(true);
            } else {
                srcAccountListBox.selectValue(editTransaction.getSrcAccountId());
                dstAccountListBox.selectValue(editTransaction.getDstAccountId());
                incomeCategoryListBox.selectValue(null);
                outcomeCategoryListBox.selectValue(null);
                transferRadioBtn.setValue(true);
            }
        } else {
            if(editTransaction.getSrcAccountId() != null) {
                if(editTransaction.getDstAccountId() != null) {
                    transferRadioBtn.setValue(true);
                    dstAccountListBox.selectValue(editTransaction.getDstAccountId());
                } else {
                    outcomeRadioBtn.setValue(true);
                    dstAccountListBox.selectValue(null);
                }
                srcAccountListBox.selectValue(editTransaction.getSrcAccountId());
                incomeCategoryListBox.selectValue(null);
                outcomeCategoryListBox.selectValue(editTransaction.getCategoryId());
            } else {
                incomeRadioBtn.setValue(true);
                srcAccountListBox.selectValue(null);
                dstAccountListBox.selectValue(editTransaction.getDstAccountId());
                incomeCategoryListBox.selectValue(editTransaction.getCategoryId());
                outcomeCategoryListBox.selectValue(null);
            }
        }
        incomeRadioBtn.setEnabled(editTransaction.isNew());
        outcomeRadioBtn.setEnabled(editTransaction.isNew());
        transferRadioBtn.setEnabled(editTransaction.isNew());

        dateBox.setValue(editTransaction.getDate());
        sumBox.setValue(StringUtils.formatSum(editTransaction.getSum(), false));
        currencyListBox.selectValue(editTransaction.getCurrencyId());
        convertCheckBox.setValue(editTransaction.getConvertCurrencyId() != null);
        if(editTransaction.getConvertCurrencyId() != null) {
            convertCurrencyListBox.selectValue(editTransaction.getConvertCurrencyId());
            convertRateBox.setValue(StringUtils.doubleToString(editTransaction.getConvertCurrencyRate()));
        }
        refreshConvertionForm();
        
        descArea.setText(editTransaction.getDescription());
        tagsCloud.setValue(editTransaction.getTags());
        approveCheckBox.setValue(editTransaction.isApproved());

        if(editTransaction.isNew()) {
            submitBtn.setText("Добавить");
        } else {
            submitBtn.setText("Сохранить");
        }
    }

    private void changeTransactionType() {
        if(incomeRadioBtn.getValue()) {
            newTransactionType = TransactionDto.Type.INCOME;
        } else if(outcomeRadioBtn.getValue()) {
            newTransactionType = TransactionDto.Type.OUTCOME;
        } else {
            newTransactionType = TransactionDto.Type.TRANSFER;
        }
    }

    private void saveEditTransaction() throws FormValidationException {
        FormValidationException saveException = null;
        editTransaction.setApproved(approveCheckBox.getValue());
        editTransaction.setDate(dateBox.getValue());
        if(dateBox.getValue() == null) {
            saveException = new FormValidationException("Дата не задана");
        }
        editTransaction.setTags(tagsCloud.getValue());
        editTransaction.setDescription(descArea.getValue());
        try {
            editTransaction.setSum(StringUtils.parseSum(sumBox.getValue()));
        } catch(AppException ex) {
            //todo: make an alert
            saveException = new FormValidationException("Cумма списания задана некорректно", ex);
        }
        editTransaction.setCurrencyId(currencyListBox.getValue());
        if(currencyListBox.getValue() == null) {
            saveException = new FormValidationException("Валюта не выбрана");
        }
        if(convertCheckBox.getValue()) {
            editTransaction.setConvertCurrencyId(convertCurrencyListBox.getValue());
            if(convertCurrencyListBox.getValue() == null) {
                saveException = new FormValidationException("Валюта конвертации не выбрана");
            }
            try {
                editTransaction.setConvertCurrencyRate(StringUtils.parseSum(convertRateBox.getValue()));
            } catch(AppException ex) {
                saveException = new FormValidationException("Курс конвертации задан некорректно", ex);
            }
        } else {
            editTransaction.setConvertCurrencyId(null);
            editTransaction.setConvertCurrencyRate(null);
        }
        if(incomeRadioBtn.getValue()) {
            editTransaction.setSrcAccountId(null);
            editTransaction.setDstAccountId(dstAccountListBox.getValue());
            editTransaction.setCategoryId(incomeCategoryListBox.getValue());
            if(dstAccountListBox.getValue() == null) {
                saveException = new FormValidationException("Счет назначения не выбран");
            }
            if(incomeCategoryListBox.getValue() == null) {
                saveException = new FormValidationException("Категория дохода не выбрана");
            }
        } else if(outcomeRadioBtn.getValue()) {
            editTransaction.setSrcAccountId(srcAccountListBox.getValue());
            editTransaction.setDstAccountId(null);
            editTransaction.setCategoryId(outcomeCategoryListBox.getValue());
            if(srcAccountListBox.getValue() == null) {
                saveException = new FormValidationException("Счет списания не выбран");
            }
            if(outcomeCategoryListBox.getValue() == null) {
                saveException = new FormValidationException("Категория расхода не выбрана");
            }
        } else {
            editTransaction.setSrcAccountId(srcAccountListBox.getValue());
            editTransaction.setDstAccountId(dstAccountListBox.getValue());
            editTransaction.setCategoryId(null);
            if(srcAccountListBox.getValue() == null) {
                saveException = new FormValidationException("Счет списания не выбран");
            }
            if(dstAccountListBox.getValue() == null) {
                saveException = new FormValidationException("Счет назначения не выбран");
            }
        }
        if(saveException != null) {
            throw saveException;
        }
    }

    public void setValue(TransactionDto transaction) {
        if(transaction == null) {
            editTransaction = newTransaction;
        } else {
            editTransaction = transaction;
        }
        refillForm();
        repaintForm();
    }

    private void submitTransaction() {
        try {
            saveEditTransaction();
            getMainService().submitTransaction(editTransaction, new AsyncCallback<Void>() {

                public void onSuccess(Void result) {
                    getEventBus().fireEvent(new TransactionChangedEvent(TransactionEditPanel.this, null));
                }

                public void onFailure(Throwable caught) {
                    fireError(caught.getMessage());
                }
            });
        } catch(AppException ex) {
            fireError(ex.getMessage());
        }
    }

    private void detachTransaction() {
        try {
            final TransactionDto originalTransaction = editTransaction;
            editTransaction = newTransaction;
            saveEditTransaction();
            getMainService().detachTransaction(editTransaction, originalTransaction.getId(), new AsyncCallback<Void>() {

                public void onSuccess(Void result) {
                    getEventBus().fireEvent(new TransactionChangedEvent(TransactionEditPanel.this, null));
                    setValue(originalTransaction);
                }

                public void onFailure(Throwable caught) {
                    fireError(caught.getMessage());
                }
            });
        } catch(AppException ex) {
            fireError(ex.getMessage());
        }
    }

    private void removeTransaction() {
        getMainService().removeTransaction(editTransaction.getId(), new AsyncCallback<Void>() {

            public void onSuccess(Void result) {
                getEventBus().fireEvent(new TransactionChangedEvent(TransactionEditPanel.this, null));
                setValue(null);
            }

            public void onFailure(Throwable caught) {
                fireError(caught.getMessage());
            }
        });
    }
}
